<!-- @format -->

<template>
  <view class="comp-model-container" v-if="animationVisible">
    <!-- 遮罩层 -->
    <view
      class="comp-model-mask"
      :class="{ 'comp-model-mask--visible': localVisible }"
      @click="handleMaskClick"
    ></view>

    <!-- 弹窗内容 -->
    <view
      class="comp-model"
      :class="{
        'comp-model--visible': localVisible,
        'comp-model--center': position === 'center' && !fullscreen,
        'comp-model--top': position === 'top',
        'comp-model--bottom': position === 'bottom',
        'comp-model--left': position === 'left',
        'comp-model--right': position === 'right',
        'comp-model--fullscreen': fullscreen,
        [`comp-model--${size}`]: size,
      }"
      :style="{
        '--duration': `${duration}ms`,
        width: width,
        height: height,
      }"
    >
      <!-- 标题区域 -->
      <view class="comp-model__header" v-if="title || $slots.header">
        <slot name="header">
          <view class="comp-model__title">{{ title }}</view>
        </slot>
        <view class="comp-model__close" v-if="showClose" @click="handleClose">
          <slot name="close-icon">
            <text class="comp-model__close-icon">×</text>
          </slot>
        </view>
      </view>

      <!-- 内容区域 -->
      <view class="comp-model__content">
        <slot></slot>
      </view>

      <!-- 底部区域 -->
      <view class="comp-model__footer" v-if="$slots.footer">
        <slot name="footer"></slot>
      </view>
    </view>
  </view>
</template>

<script setup lang="ts">
  import { ref, watch, onMounted, onBeforeUnmount } from 'vue';
  import type { ModelProps } from './types';

  // 定义Props
  const props = withDefaults(defineProps<ModelProps>(), {
    modelValue: false,
    title: '',
    width: '',
    height: '',
    position: 'center',
    fullscreen: false,
    showClose: true,
    closeOnClickMask: true,
    duration: 300,
    size: 'medium',
    zIndex: 2000,
    customClass: '',
    appendToBody: false,
  });

  // 定义事件
  const emit = defineEmits<{
    (e: 'update:modelValue', visible: boolean): void;
    (e: 'open'): void;
    (e: 'opened'): void;
    (e: 'close'): void;
    (e: 'closed'): void;
  }>();

  // 内部状态
  const localVisible = ref(false);
  const animationVisible = ref(false);
  let timer: number | null = null;

  // 打开弹窗
  const open = () => {
    if (animationVisible.value) return;

    animationVisible.value = true;
    // 下一帧开始动画
    setTimeout(() => {
      localVisible.value = true;
      emit('open');

      // 动画结束后触发opened事件
      timer = setTimeout(() => {
        emit('opened');
      }, props.duration) as unknown as number;
    }, 10);
  };

  // 关闭弹窗
  const close = () => {
    if (!animationVisible.value) return;

    localVisible.value = false;
    emit('close');

    // 动画结束后隐藏元素并触发closed事件
    timer = setTimeout(() => {
      animationVisible.value = false;
      emit('closed');
    }, props.duration) as unknown as number;
  };

  // 处理遮罩点击
  const handleMaskClick = () => {
    if (props.closeOnClickMask) {
      updateVisible(false);
    }
  };

  // 处理关闭按钮点击
  const handleClose = () => {
    updateVisible(false);
  };

  // 更新显示状态
  const updateVisible = (visible: boolean) => {
    emit('update:modelValue', visible);
    if (!visible) {
      close();
    }
  };

  // 组件挂载时初始化
  onMounted(() => {
    if (props.modelValue) {
      open();
    }
  });

  // 组件卸载前清理定时器
  onBeforeUnmount(() => {
    if (timer !== null) {
      clearTimeout(timer);
      timer = null;
    }
  });

  // 监听modelValue变化
  watch(
    () => props.modelValue,
    (val) => {
      if (val) {
        open();
      } else {
        close();
      }
    },
    { immediate: true },
  );
  // 暴露方法
  defineExpose({
    open,
    close,
  });
</script>

<style scoped>
  .comp-model-container {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: v-bind(zIndex);
    overflow: hidden;
  }

  .comp-model-mask {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0);
    transition: background-color var(--duration, 300ms) ease;
  }

  .comp-model-mask--visible {
    background-color: rgba(0, 0, 0, 0.5);
  }

  .comp-model {
    position: absolute;
    background-color: #fff;
    border-radius: 16rpx;
    box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    transition: all var(--duration, 300ms) ease;
  }

  /* 位置样式 */
  .comp-model--center {
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) scale(0.8);
    opacity: 0;
  }

  .comp-model--center.comp-model--visible {
    transform: translate(-50%, -50%) scale(1);
    opacity: 1;
  }

  .comp-model--top {
    top: 0;
    left: 0;
    right: 0;
    transform: translateY(-100%);
    border-radius: 0 0 16rpx 16rpx;
    width: 100% !important;
  }

  .comp-model--top.comp-model--visible {
    transform: translateY(0);
  }

  .comp-model--bottom {
    bottom: 0;
    left: 0;
    right: 0;
    transform: translateY(100%);
    border-radius: 16rpx 16rpx 0 0;
    width: 100% !important;
  }

  .comp-model--bottom.comp-model--visible {
    transform: translateY(0);
  }

  .comp-model--left {
    top: 0;
    left: 0;
    bottom: 0;
    transform: translateX(-100%);
    border-radius: 0 16rpx 16rpx 0;
    height: 100% !important;
    max-height: 100% !important;
  }

  .comp-model--left.comp-model--visible {
    transform: translateX(0);
  }

  .comp-model--right {
    top: 0;
    right: 0;
    bottom: 0;
    transform: translateX(100%);
    border-radius: 16rpx 0 0 16rpx;
    height: 100% !important;
    max-height: 100% !important;
  }

  .comp-model--right.comp-model--visible {
    transform: translateX(0);
  }

  /* 全屏样式 */
  .comp-model--fullscreen {
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100vw !important;
    height: 100vh !important;
    border-radius: 0;
    max-height: 100vh !important;
  }

  /* 尺寸样式 */
  .comp-model--small {
    width: 80%;
    max-width: 600rpx;
  }

  .comp-model--medium {
    width: 90%;
    max-width: 800rpx;
  }

  .comp-model--large {
    width: 95%;
    max-width: 1000rpx;
  }

  /* 内部结构样式 */
  .comp-model__header {
    position: relative;
    padding: 32rpx;
    border-bottom: 1px solid #eee;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .comp-model__title {
    font-size: 32rpx;
    font-weight: 500;
    color: #333;
    text-align: center;
  }

  .comp-model__close {
    position: absolute;
    right: 32rpx;
    top: 50%;
    transform: translateY(-50%);
    cursor: pointer;
  }

  .comp-model__close-icon {
    font-size: 40rpx;
    color: #999;
    line-height: 1;
  }

  .comp-model__content {
    flex: 1;
    padding: 32rpx;
    overflow-y: auto;
  }

  .comp-model__footer {
    padding: 24rpx 32rpx;
    border-top: 1px solid #eee;
    display: flex;
    justify-content: flex-end;
  }

  /* 条件编译样式 */
  /* #ifdef H5 */
  .comp-model {
    max-height: 90vh;
  }
  /* #endif */

  /* #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU || MP-TOUTIAO || MP-QQ */
  .comp-model {
    max-height: 85vh;
  }
  /* #endif */

  /* #ifdef APP-PLUS */
  .comp-model {
    max-height: 80vh;
  }
  /* #endif */
</style>
